package com.fitz.bean;

import com.fitz.util.CovertUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author gousili
 * Created  on 2020/7/6
 */
public class DefaultListableBeanFactory implements BeanFactory {

    private final List<BeanDefinition> beanDefines = new ArrayList<>();

    private Map<String,Object> beanMap = new ConcurrentHashMap<>();

    @Override
    public Object getBean(String name) {
        return beanMap.get(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> clazz) {
        return null;
    }

    @Override
    public boolean containsBean(String name) {
        return false;
    }

    public List<BeanDefinition> getBeanDefines() {
        return beanDefines;
    }

    public void addBeanDefines(List<BeanDefinition> beanDefines) {
        this.beanDefines.addAll(beanDefines);
    }

    public Map<String, Object> getBeanMap() {
        return beanMap;
    }

    /**
     * 实例化bean
     */
    public void initBeans(){
        if (CollectionUtils.isNotEmpty(beanDefines)){
            for (BeanDefinition beanDefinition : beanDefines){
                if (StringUtils.isBlank(beanDefinition.getClassName())){
                    throw new IllegalArgumentException("class name can not be null");
                }
                try {
                    beanMap.put(beanDefinition.getBeanId(),Class.forName(beanDefinition.getClassName()).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 为bean对象的属性注入值
     */
    public void injectObject(){
        for (BeanDefinition beanDefinition : beanDefines){
            Object bean = beanMap.get(beanDefinition.getBeanId());
            if (null != bean){
                try {
                    BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
                    setBeanProperty(beanDefinition,bean,pds);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 设置成员变量
     * @param beanDefinition
     * @param bean
     * @param pds
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void setBeanProperty(BeanDefinition beanDefinition,Object bean, PropertyDescriptor[] pds)
            throws InvocationTargetException, IllegalAccessException {
        for (PropertyDefinition propertyDefinition : beanDefinition.getPropertyDefinitions()){
            for (PropertyDescriptor propertyDescriptor : pds){
                if (propertyDefinition.getName().equals(propertyDescriptor.getName())){
                    beanSetInvoke(bean,propertyDefinition,propertyDescriptor);
                    break;
                }
            }
        }
    }

    /**
     * 设置字段
     * @param bean
     * @param propertyDefinition
     * @param propertyDescriptor
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void beanSetInvoke(Object bean,PropertyDefinition propertyDefinition,PropertyDescriptor propertyDescriptor)
            throws InvocationTargetException, IllegalAccessException {
        Method setter = propertyDescriptor.getWriteMethod();
        if (null != setter){
            Object value = null;
            if (StringUtils.isNotBlank(propertyDefinition.getRef())){
                value = beanMap.get(propertyDefinition.getRef());
            }else {
                value = CovertUtil.convert(propertyDefinition.getValue(),propertyDescriptor.getPropertyType());
            }
            setter.setAccessible(true);
            setter.invoke(bean,value);
        }
    }
}
